package top.hserver.gateway.core;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.*;
import lombok.extern.slf4j.Slf4j;
import top.hserver.core.ioc.IocUtil;
import top.hserver.gateway.core.bean.Proxy;
import top.hserver.gateway.core.bean.RouterInfo;
import top.hserver.gateway.core.exception.GateWayException;
import top.hserver.gateway.core.node.Manager;
import top.hserver.gateway.core.node.ServerNodeManager;
import top.hserver.gateway.core.statistics.SimpleStatistics;
import top.hserver.gateway.core.strategy.LoadBalancingStrategy;
import top.hserver.gateway.core.strategy.ServerNode;
import top.hserver.gateway.core.strategy.Strategy;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicInteger;


/**
 * @author hxm
 */
@Slf4j
public class ProxyHandler {

  static Proxy builder(ChannelHandlerContext ctx, Object msg, Proxy proxy, FrontendHandler frontendHandler) {
    proxy.setMsg(msg);
    proxy.setCtx(ctx);
    proxy.setFrontendHandler(frontendHandler);
    if (msg instanceof HttpRequest){
      proxy.setHttpRequest((HttpRequest) msg);
    }
    return proxy;
  }

  static Proxy proxyInfo(Proxy proxy) {
    Strategy<ServerNode> strategy = IocUtil.getBean(LoadBalancingStrategy.class);
    HttpRequest msg = (HttpRequest) proxy.getMsg();
    Manager manager = IocUtil.getBean(ServerNodeManager.class);
    RouterInfo routerInfo = manager.getRouterIdByUrl(msg.uri());
    ServerNode serverNode=null;
    switch (routerInfo.getStrategyType()){
      case 轮询法:
        serverNode = strategy.polling(routerInfo.getRouterId());
        break;
      default:
        serverNode = strategy.random(routerInfo.getRouterId());
        break;
    }
    proxy.setServerNode(serverNode);
    return proxy;
  }

  static Proxy statistics(Proxy proxy) {
    HttpRequest httpRequest = proxy.getHttpRequest();
    if (httpRequest!=null){
      //开始统计
      SimpleStatistics bean = IocUtil.getBean(SimpleStatistics.class);
      bean.uv(proxy.getCtx());
      bean.pv();
      bean.platform(httpRequest);
    }
    return proxy;
  }


  static Proxy filter(Proxy proxy) {
    return proxy;
  }


  static Proxy exception(Throwable throwable) {
    Proxy proxy = new Proxy();
    proxy.setThrowable(throwable);
    return proxy;
  }

  static void endGateWay(CompletableFuture<Proxy> fu, Proxy proxy, ChannelHandlerContext ctx,Object msg) {

    /**
     * 是否有异常
     */
    if (proxy.getThrowable() != null) {
      proxy.setCtx(ctx);
      //一定要释放，不释放就内存泄漏
      proxy.setMsg(msg);
      log.error(proxy.getThrowable().getMessage());
      IocUtil.getBean(GateWayOut.class).out(new GateWayException(HttpResponseStatus.SERVICE_UNAVAILABLE, "网关异常", proxy.getThrowable()), proxy);
    }

    if (proxy.getServerNode() == null) {
      IocUtil.getBean(GateWayOut.class).out(new GateWayException(HttpResponseStatus.SERVICE_UNAVAILABLE, "拒绝访问"), proxy);
    } else {
      if (proxy.getFrontendHandler().outboundChannel == null) {
        final Channel inboundChannel = proxy.getCtx().channel();
        Bootstrap b = new Bootstrap();
        b.group(inboundChannel.eventLoop());
        b.channel(NioSocketChannel.class).handler(new ChannelInitializer<Channel>() {
          @Override
          protected void initChannel(Channel ch) {
            ChannelPipeline pipeline = ch.pipeline();
            pipeline.addLast(new HttpClientCodec());
            pipeline.addLast(new HttpObjectAggregator(Integer.MAX_VALUE));
            pipeline.addLast(new BackendHandler(inboundChannel));
          }
        });
        try {
          final AtomicInteger count = new AtomicInteger(0);
          ChannelFuture f = b.connect(proxy.getServerNode().getInetSocketAddress()).addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture future) throws Exception {
              if (future.isSuccess()) {
                /**
                 * 添加连接数
                 */
                IocUtil.getBean(ServerNodeManager.class).addConnect(proxy.getServerNode().getRouterId(), proxy.getServerNode().getNodeId());
                future.channel().writeAndFlush(proxy.getMsg());
              } else {
                if (count.incrementAndGet() > proxy.getServerNode().getErrorNum()) {
                  future.channel().close();
                  /**
                   * 对当前节点打标，标识程序不健康哦.
                   */
                  IocUtil.getBean(ServerNodeManager.class).markUnhealthy(proxy.getServerNode().getRouterId(), proxy.getServerNode().getNodeId());
                  //检查备用服务器，替换上线.


                  //输出相关错误数据
                  IocUtil.getBean(GateWayOut.class).out(new GateWayException(HttpResponseStatus.GATEWAY_TIMEOUT, "网关连接远端服务器超时", future.cause()), proxy);
                } else {
                  b.connect(proxy.getServerNode().getInetSocketAddress()).addListener(this);
                }
              }
            }
          });
          proxy.getFrontendHandler().outboundChannel = f.channel();
        } catch (Exception e) {
          e.printStackTrace();
        }
      } else {
        proxy.getFrontendHandler().read(proxy.getCtx(), proxy.getMsg());
      }
    }
    fu.complete(null);
  }
}
